// RequestManager.cpp
#include "RequestManager.h"
#include "../global_logger.h"

RequestManager::RequestManager(QObject* parent)
    : QObject(parent), m_nextReqId(0) {
    LOG_INFO("RequestManager 初始化完成");
}

/**
 * @brief 发送一个请求到指定的TCP套接字，并设置回调和超时处理。
 *
 * 此函数用于向远程服务器发送带有特定命令码的数据请求，并注册一个回调函数以处理响应。
 * 同时设置了超时机制，如果在指定时间内未收到响应，则会触发超时处理逻辑。
 *
 * @param socket 指向SCStatusTcp对象的指针，表示用于通信的TCP连接。
 * @param cmd 命令码，标识请求的类型。
 * @param data 要发送的数据内容。
 * @param callback 响应到达时调用的回调函数，参数为接收到的数据。
 * @param timeoutMs 超时时间（毫秒），超过该时间未收到响应则认为请求失败。
 */
void RequestManager::sendRequest(SCStatusTcp* socket, 
                                uint16_t cmd, 
                                const QByteArray &data,
                                std::function<void(const QByteArray&)> callback,
                                int timeoutMs) 
{
    LOG_DEBUG("发送请求 - 命令码: {}, 数据大小: {} 字节, 超时: {}ms", cmd, data.size(), timeoutMs);
    
    if (!socket) {
        LOG_ERROR("发送请求失败: 无效的socket");
        callback(QByteArray());
        return;
    }

    // 检查socket连接状态
    if (!socket->tcpSocket() || socket->tcpSocket()->state() != QAbstractSocket::ConnectedState) {
        LOG_WARN("发送请求失败: Socket未连接，命令码: {}", cmd);
        callback(QByteArray());
        return;
    }

    QMutexLocker locker(&m_mutex);
    
    // 如果已存在相同 cmd 的请求，先清理它
    if (m_pendingRequests.contains(cmd)) {
        auto oldCtx = m_pendingRequests.take(cmd);
        oldCtx.timer->stop();
        QTimer* timerToDelete = oldCtx.timer;
        locker.unlock();
        timerToDelete->deleteLater();
        locker.relock();
        LOG_DEBUG("清理旧的请求 - 命令码: {}", cmd);
    }

    const uint16_t reqId = m_nextReqId++;

    // 创建请求上下文
    RequestContext ctx{
        cmd,
        data,
        new QTimer(this),
        callback
    };

    // 设置定时器
    ctx.timer->setSingleShot(true);
    QObject::connect(ctx.timer, &QTimer::timeout, this, [this, cmd]() {
        handleTimeout(cmd);
    });
    ctx.timer->start(timeoutMs);

    // 保存请求，使用 cmd 作为键
    m_pendingRequests[cmd] = ctx;

    // 异步发送
    socket->asyncWriteTcpData(cmd, data, reqId);
    LOG_DEBUG("请求已发送 - 命令码: {}, 请求ID: {}", cmd, reqId);

    // 确保每个 socket 只连接一次响应信号
    if (!m_connectedSockets.contains(socket)) {
        QObject::connect(socket, &SCStatusTcp::responseReceived, this,
            &RequestManager::handleResponse, Qt::DirectConnection);
        m_connectedSockets.insert(socket);
    }
}

/**
 * @brief 处理请求超时事件
 * 
 * 当请求超时发生时，该函数会被调用。它会从待处理请求列表中移除对应的请求，
 * 删除相关的定时器，并执行回调函数返回空数据表示超时。
 * 
 * @param cmd 命令ID，用于标识哪个请求发生了超时
 */
void RequestManager::handleTimeout(uint16_t cmd) {
    LOG_WARN("请求超时 - 命令码: {}", cmd);
    
    QMutexLocker locker(&m_mutex);
    if (m_pendingRequests.contains(cmd)) {
        auto ctx = m_pendingRequests.take(cmd);
        // 不在这里直接 deleteLater，避免在锁中做过多操作
        QTimer* timerToDelete = ctx.timer;
        std::function<void(const QByteArray&)> callback = ctx.callback;
        locker.unlock(); // 先解锁
        timerToDelete->deleteLater(); // 在无锁状态下删除
        try {
            callback(QByteArray()); // 超时返回空
        } catch (...) {
            LOG_ERROR("处理超时回调时发生异常 - 命令码: {}", cmd);
        }
        LOG_DEBUG("超时请求处理完成 - 命令码: {}", cmd);
    }
}

/**
 * @brief 处理服务器响应数据
 * 
 * 该函数用于处理从服务器接收到的响应数据，根据命令码查找对应的待处理请求，
 * 停止超时定时器并执行回调函数。
 * 
 * @param recvCmd 接收到的命令码，实际命令码为 recvCmd - 10000
 * @param recvReqId 接收到的请求ID（当前未使用）
 * @param data 服务器返回的数据内容
 */
void RequestManager::handleResponse(uint16_t recvCmd, uint16_t recvReqId, const QByteArray &data) {
    Q_UNUSED(recvReqId)
    // 根据 cmd 和 recvCmd 的关系 (recvCmd = cmd + 10000) 查找对应的请求
    uint16_t originalCmd = recvCmd - 10000;
    LOG_DEBUG("收到响应 - 原始命令码: {}, 接收命令码: {}, 数据大小: {} 字节", 
                        originalCmd, recvCmd, data.size());
    
    QMutexLocker locker(&m_mutex);

    if (m_pendingRequests.contains(originalCmd)) {
        auto ctx = m_pendingRequests.take(originalCmd);
        ctx.timer->stop();
        QTimer* timerToDelete = ctx.timer;
        std::function<void(const QByteArray&)> callback = ctx.callback;
        locker.unlock(); // 先解锁
        timerToDelete->deleteLater(); // 在无锁状态下删除
        try {
            callback(data); // 将接收到的数据传递给回调函数
        } catch (...) {
            LOG_ERROR("处理响应回调时发生异常 - 命令码: {}", originalCmd);
        }
        //LOG_DEBUG("响应处理完成 - 命令码: {}, 数据大小: {} 字节", originalCmd, data.size());
    } else {
        LOG_WARN("收到未匹配的响应 - 命令码: {}", originalCmd);
    }
}